package cn.wolf.netty.codec;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.protobuf.ProtobufDecoder;
import io.netty.handler.codec.protobuf.ProtobufEncoder;

/**
 * 原理:
 * Netty 抽象出两组线程池 BossGroup 专门负责接收客户端的连接，WorkerGroup 专门负责网络的读写
 * BossGroup 和 WorkerGroup 类型都是 NioEventLoopGroup
 * NioEventLoopGroup 相当于一个事件循环组，这个组中含有多个事件循环，每一个事件循环是 NioEventLoop
 * NioEventLoop 表示一个不断循环的执行处理任务的线程，每个 NioEventLoop 都有一个 Selector，用于监听绑定在其上的 socket 的网络通讯
 * NioEventLoopGroup 可以有多个线程，即可以含有多个 NioEventLoop
 * 每个 BossNioEventLoop 循环执行的步骤有 3 步
 * 轮询 accept 事件
 * 处理 accept 事件，与 client 建立连接，生成 NioScocketChannel，并将其注册到某个 worker NIOEventLoop 上的 Selector
 * 处理任务队列的任务，即 runAllTasks
 * 每个 Worker NIOEventLoop 循环执行的步骤
 * 轮询 read，write 事件
 * 处理 I/O 事件，即 read，write 事件，在对应 NioScocketChannel 处理
 * 处理任务队列的任务，即 runAllTasks
 * 每个 Worker NIOEventLoop 处理业务时，会使用 pipeline（管道），pipeline 中包含了 channel，即通过 pipeline 可以获取到对应通道，管道中维护了很多的处理器
 * <p>
 * netty 服务端
 */
public class NettyServer {
    public static void main(String[] args) throws Exception {

        //1.创建BossGroup 和 WorkerGroup  线程组
        //bossExecutors 只负责处理连接请求
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        //workerExecutors 负责处理具体的客户端业务
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
            //2.创建服务器端启动对象
            ServerBootstrap bootstrap = new ServerBootstrap();

            //3.使用链式编程进行参数设置
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class) //设置通道类型为NioServerSocketChannel
                    .option(ChannelOption.SO_BACKLOG, 128) //设置TCP中的连接队列大小
                    .childOption(ChannelOption.SO_KEEPALIVE, true) // 设置保持活动连接状态
                    .childHandler(new ChannelInitializer<SocketChannel>() { // 创建通道测试对象(匿名对象)
                        //给pipeline(管道) 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            //在pipeline管道中加入protobuf 解码器,需要指定解码对象类型
                            pipeline.addLast("decoder",new ProtobufDecoder(StudentPOJO.Student
                                    .getDefaultInstance()));
                            //设置管道处理器为自定义处理器
                            pipeline.addLast(new NettyServerHandler());
                        }
                    }); // 给线程组workerGroup 的EventLoop 对应的管道设置处理器
            System.out.println("---------- 服务器 is ready ------------");

            //4.服务器启动对象绑定端口并且同步,启动服务器
            ChannelFuture sync = bootstrap.bind(6668).sync();

            //注冊一个监听器,监听绑定端口是否成功
            sync.addListener((ChannelFutureListener) channelFuture -> {
                if (channelFuture.isSuccess()) {
                    System.out.println("绑定端口6668 成功!");
                } else {
                    System.out.println("绑定端口6668 失败!");
                }
            });

            //5.对关闭通道进行监听
            sync.channel().closeFuture().sync();
        } finally {
            // 优雅关闭线程组资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
